package uz.rt.dicom.dictionary;

import uz.rt.dicom.ValueMultiplicity;
import uz.rt.dicom.ValueRepresentation;
import uz.rt.dicom.tag.DicomTag;

import java.util.Iterator;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DicomDictionary implements IDicomDictionary
{
// ------------------------------ FIELDS ------------------------------

    /** DicomDictionary logger */
    private final static Logger logger = Logger.getLogger(DicomDictionary.class.getName());
    /** TreeMap storing the dicom dictionary tags. */
    private final TreeMap<DicomTag, DictionaryTag> dicomTags;
    /** TreeMap storing the dicom dictionary sopclasses tags. */
    private final TreeMap<String, DictionarySopclassTag> sopclassTags;
    /** TreeMap storing the dicom dictionary transfer syntax tags. */
    private final TreeMap<String, DictionaryTransferSyntaxTag> transferSyntaxTags;

// -------------------------- STATIC METHODS --------------------------

    public static DicomDictionary getInstance()
    {
        return FactorySingleton.getInstance();
    }

// --------------------------- CONSTRUCTORS ---------------------------

    private DicomDictionary()
    {
        dicomTags = new TreeMap<DicomTag, DictionaryTag>();
        sopclassTags = new TreeMap<String, DictionarySopclassTag>();
        transferSyntaxTags = new TreeMap<String, DictionaryTransferSyntaxTag>();
    }

// ------------------------ CANONICAL METHODS ------------------------

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof DicomDictionary)) return false;

        DicomDictionary that = (DicomDictionary) o;

        return !(dicomTags != null ? !dicomTags
                .equals(that.dicomTags) : that.dicomTags != null) && !(sopclassTags != null ? !sopclassTags
                .equals(that.sopclassTags) : that.sopclassTags != null) && !(transferSyntaxTags != null ? !transferSyntaxTags
                .equals(that.transferSyntaxTags) : that.transferSyntaxTags != null);
    }

    @Override
    public int hashCode()
    {
        int result = dicomTags != null ? dicomTags.hashCode() : 0;
        result = 31 * result + (sopclassTags != null ? sopclassTags.hashCode() : 0);
        result = 31 * result + (transferSyntaxTags != null ? transferSyntaxTags.hashCode() : 0);
        return result;
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface IDicomDictionary ---------------------

    public void add(IDictionaryItem item)
    {
        if (item instanceof DictionaryTag) addTag((DictionaryTag) item);
        else if (item instanceof DictionarySopclassTag) addSopclass((DictionarySopclassTag) item);
        else if (item instanceof DictionaryTransferSyntaxTag) addTransferSyntax((DictionaryTransferSyntaxTag) item);
        else logger.log(Level.WARNING,
                        "Trying to add an unsupported IDictionaryItem instance. " +
                                "For the current support IDictionaryItem implementation check the JavaDoc.");
    }

    public void addSopclass(DictionarySopclassTag sopclassTag)
    {
        sopclassTags.put(sopclassTag.getKey(), sopclassTag);
        logger.log(Level.INFO, "Added a SopclassTag to the dicom dictionary.");
    }

    public void addTag(DictionaryTag tag)
    {
        dicomTags.put(tag.getKey(), tag);
        logger.log(Level.INFO, "Added a tag " + tag.getKey().toString() + " to the dicom dictionary.");
    }

    public void addTransferSyntax(DictionaryTransferSyntaxTag transferSyntaxTag)
    {
        transferSyntaxTags.put(transferSyntaxTag.getKey(), transferSyntaxTag);
        logger.log(Level.INFO, "Added a TransferSyntaxTag to the dicom dictionary.");
    }

    public Iterator<DictionarySopclassTag> getSopclassIterator()
    {
        return sopclassTags.values().iterator();
    }

    public TreeMap getSopclasses()
    {
        return sopclassTags;
    }

    public Iterator<DictionaryTag> getTagIterator()
    {
        return dicomTags.values().iterator();
    }

    public ValueMultiplicity getTagVM(DicomTag tag)
    {
        if (tag == null)
        {
            logger.log(Level.WARNING, "Unable to get value multiplicity for a NULL DicomTag.");
            return null;
        } else
        {
            if (!dicomTags.containsKey(tag))
            {
                logger.log(Level.WARNING,
                           "Unable to get value multiplicity for a DicomTag that is not present in the dictionary.");
                return null;
            }
        }
        return dicomTags.get(tag).getVM();
    }

    public ValueRepresentation getTagVR(DicomTag tag)
    {
        if (tag == null)
        {
            logger.log(Level.WARNING, "Unable to get value representation for a NULL DicomTag.");
            return null;
        } else
        {
            if (!dicomTags.containsKey(tag))
            {
                logger.log(Level.WARNING,
                           "Unable to get value representation for a DicomTag that is not present in the dictionary.");
                return null;
            }
        }
        return dicomTags.get(tag).getVR();
    }

    public TreeMap getTags()
    {
        return dicomTags;
    }

    public Iterator<DictionaryTransferSyntaxTag> getTransferSyntaxIterator()
    {
        return transferSyntaxTags.values().iterator();
    }

    public TreeMap getTransferSyntaxes()
    {
        return transferSyntaxTags;
    }

// -------------------------- INNER CLASSES --------------------------

    /**
     * The Factory singleton class storing a static instance of the DicomDictionary class. The creation of the new
     * instance is done in a static part of the code and can only be executed once.
     */
    static class FactorySingleton
    {
        /** Instance of the ElementFactory class */
        static final DicomDictionary instance;

        static
        {
            instance = new DicomDictionary();
        }

        /**
         * Get the instance of the DicomDictionary class.
         *
         * @return Instance of the DicomDictionary class.
         */
        static DicomDictionary getInstance()
        {
            return instance;
        }
    }
}
